package server

import (
	"context"
	"fmt"
	"net"
	"os"

	"google.golang.org/grpc"
	"google.golang.org/grpc/metadata"
	"wowCoupon.com/logger"
	"wowCoupon.com/pkg/auth"
	pb "wowCoupon.com/rpcserver/customer/cer"
	"wowCoupon.com/service/couponMetaService"
	"wowCoupon.com/service/userService"
	"wowCoupon.com/setting"
)

func Serve() {
	//Listen the port
	hostname, _ := os.Hostname()
	fmt.Println(hostname)

	lis, err := net.Listen("tcp", fmt.Sprintf("%s:%d", hostname, setting.RpcServerCustomerSetting.Port))
	if err != nil {
		logger.Fatal("rpc customer server listener error: %s", err.Error())
		panic(err)
	}

	// config TLS cert
	//creds, err := credentials.NewServerTLSFromFile("cert/server.crt", "cert/server.key")
	//if err != nil {
	//logger.Fatal("rpc customer server config TLS error: %s", err.Error())
	//panic(err)
	//}

	//Define rpc server
	s := grpc.NewServer() //grpc.Creds(creds))
	pb.RegisterCerServer(s, &customerServer{})

	//Run the rpc server with net.Listener
	if err := s.Serve(lis); err != nil {
		logger.Fatal("rpc customer server grpc server error: %s", err.Error())
		panic(err)
	}

	logger.Info("rpc customer server run on: %s", fmt.Sprintf("%s:%d", hostname, setting.RpcServerCustomerSetting.Port))
}

type customerServer struct {
	pb.UnimplementedCerServer
}

func (b *customerServer) Login(ctx context.Context, in *pb.LoginRequest) (*pb.LoginReply, error) {
	var user userService.User = userService.User{
		Username: in.Username,
		Password: in.Password,
	}

	// Credential successfully
	if user.VerifyCredentialInfo() {
		token := auth.CreateToken(in.Username)
		return &pb.LoginReply{
			Code:  200,
			Msg:   "OK",
			Token: token,
		}, nil
	}

	// Credential fail
	return &pb.LoginReply{
		Code:  403,
		Msg:   "Fobbiden, username or password is not correct",
		Token: "",
	}, nil
}

func (b *customerServer) Rob(ctx context.Context, in *pb.RobRequest) (*pb.RobReply, error) {
	// authentication
	tokenString, err := getTokenFromContext(ctx)
	if err != nil {
		logger.Error("rpc customer server authentication error: %s", err.Error())
		return &pb.RobReply{Code: 403, Msg: "Fobbiden"}, err
	}

	if verified, err := auth.CheckToken(tokenString); !verified {
		logger.Error("rpc customer server authentication error: invalid token; %s", err.Error())
		return &pb.RobReply{Code: 403, Msg: "Fobbiden"}, err
	}

	// process
	userID := in.GetUserId()
	businessID := in.GetBusinessId()
	goodID := in.GetGoodId()
	couponID := in.GetCouponId()

	c := &couponMetaService.CouponMeta{
		UserId:     userID,
		ID:         couponID,
		BusinessId: businessID,
		GoodId:     goodID,
	}

	//c.UserIdList = flow.NewBloomFilter(tools.GenRedisKeyUserIdList(c.BusinessId, c.GoodId, c.ID), 0, 0)

	robResult, _ := c.Rob()

	if robResult.Status != couponMetaService.RobOK {
		return &pb.RobReply{Code: 410, Msg: robResult.String()}, nil
	}

	return &pb.RobReply{Code: 200, Msg: robResult.String()}, nil
}

func (b *customerServer) GetTemplate(ctx context.Context, in *pb.TemplateRequest) (*pb.TemplateReply, error) {
	// authentication
	tokenString, err := getTokenFromContext(ctx)
	if err != nil {
		logger.Error("rpc customer server authentication error: %s", err.Error())
		return &pb.TemplateReply{Code: 403, Msg: "Fobbiden"}, err
	}

	if verified, err := auth.CheckToken(tokenString); !verified {
		logger.Error("rpc customer server authentication error: invalid token; %s", err.Error())
		return &pb.TemplateReply{Code: 403, Msg: "Fobbiden"}, err
	}

	// process
	c := &couponMetaService.CouponMeta{
		ID: in.GetCouponId(),
	}

	template, err := c.GetTemplateForCustomer()

	if err != nil {
		return nil, err
	}

	result := &pb.TemplateReply{
		Code: 200,
		Msg:  "OK",

		CouponId:       template.ID,
		GoodId:         template.GoodId,
		Type:           template.Type,
		Title:          template.Title,
		Comments:       template.Comments,
		ValidStartTime: template.ValidStartTime,
		ValidEndTime:   template.ValidEndTime,
		Status:         template.Status,
	}

	return result, nil
}

// get token from request context
func getTokenFromContext(ctx context.Context) (string, error) {
	md, ok := metadata.FromIncomingContext(ctx)
	if !ok {
		return "", fmt.Errorf("ErrNoMetadataInContext")
	}
	// md 的类型是 type MD map[string][]string
	token, ok := md["authorization"]
	if !ok || len(token) == 0 {
		return "", fmt.Errorf("ErrNoAuthorizationInMetadata")
	}
	// 因此，token 是一个字符串数组，我们只用了 token[0]
	return token[0], nil
}
